﻿//#define NEWCODE 0

using AiAsteriods.Game.State;


namespace AiAsteriods.Game.Entities.Agent {
  class MNode {
    public static int numActions;
    private static float[] weights = new float[2] { 1.0f, 2.0f }; // weight vector = {estscore weight, deadly weight}
    ActionType action;
    float estScore;
    int numDeaths;
    MNode[] children;
    bool isDeadly;
    GameState state;
    private int numSubNodes;

    public float[] GetChildScores() {
      float[] childScores = new float[numActions];
      if (children != null) {
        for (int i = 0; i < numActions; i++) {
          float score = children[i].estScore * weights[0] - children[i].numDeaths * weights[1];
          if (score > 0)
            childScores[i] = score;
          else
            childScores[i] = 0;
        }
      }
      return childScores;
    }

    public static int MaxDepth {
      get { return Global.GameStart.Config.MaxDepth; }
    }

    public MNode() {
      numDeaths = 0;
      numSubNodes = 0;
      MNode.numActions = Global.GameStart.Config.MaxActions;
    }

    public int CountSubNodes() {
      this.numSubNodes = 0;
      CountSubNodes(this);
      return this.numSubNodes;
    }

    private void CountSubNodes(MNode node) {
      if (node != null && node.children != null) {
        foreach (MNode child in node.children) {
          this.numSubNodes++;
          CountSubNodes(child);
        }
      }
    }

    public void Reset() {
      action = ActionType.BRAKE;
      estScore = 0;
      numDeaths = 0;
      numSubNodes = 0;
      children = null;
      isDeadly = false;
      state = null;
    }

    private static GameState UpdateState(ActionType action, GameState state) {
      switch (action) {
        case ActionType.ACCEL:
          state.Player1.Accel();
          break;
        case ActionType.BRAKE:
          state.Player1.Brake();
          break;
        case ActionType.FLEE:
          state.Player1.Flee();
          break;
        case ActionType.LEFT:
          state.Player1.Left();
          break;
        case ActionType.RIGHT:
          state.Player1.Right();
          break;
        case ActionType.SHOOT:
          state.Player1.Shoot();
          break;
        case ActionType.ORIGIN:
          state.Player1.Origin();
          break;
      }

      state.UpdateState(0);

      return state;
    }

    public static MNode Build(MNode node) {
      node.state = (GameState)Global.GameStart.CurrentState;
      if (node.state.Player1.isDummy)
        return null;
      else
        return _Build(node, 0);
    }

    private static MNode _Build(MNode node, int depth) {
#if NEWCODE
      if (depth == MaxDepth) { //if tree gets to maximum depth, return something
        node.estScore = depth;
      }
      else { //creates the tree structure and assigns an action and state for each child.
        node.children = new MNode[numActions];  //creates and assigns numActions children nodes to node.children
        float maxScore = float.MinValue;
        int numDeaths = 0;
        for (int i = 0; i < numActions; i++) {
          MNode child = MNodeFactory.Get();
          child.action = (ActionType)i;
          child.state = (GameState)node.state.Clone(); //added this line ~ Daniel (with a little assitance from kevin *cough cough*).
          child.state = UpdateState(child.action, child.state);
          child.isDeadly = child.state.Player1.IsDead;
          if (!child.isDeadly)
            child = _Build(child, depth + 1);  //recursive call.

          if (child.estScore > maxScore)
            maxScore = child.estScore;
          if (child.isDeadly)
            numDeaths++;
          numDeaths += child.numDeaths;
          node.children[i] = child;
        }

        node.estScore = maxScore;
        node.numDeaths += numDeaths;
      }
      return node;
#else
      if (depth != 0) {
        node.state = UpdateState(node.action, node.state);
        if (node.state.Player1.currentLife <= 0) {
          node.isDeadly = true;
          node.estScore = -100;
          return node;
        }
        else
          node.isDeadly = false;
      }

      if (depth == MaxDepth) { //if tree gets to maximum depth or if node is deadly, return something
        node.estScore = depth;
      }
      else { //creates the tree structure and assigns an action and state for each child.
        node.children = new MNode[numActions];  //creates and assigns 6 children nodes (accelerate, brake, flee, left, right, shoot) to node.children
        float maxScore = float.MinValue;
        int numDeaths = 0;
        for (int i = 0; i < numActions; i++) {
          MNode child = MNodeFactory.Get();
          child.action = (ActionType)i;
          child.state = (GameState)node.state.Clone();
          child = _Build(child, depth + 1);  //recursive call.
          if (child.estScore > maxScore)
            maxScore = child.estScore;
          if (child.isDeadly)
            numDeaths++;
          node.children[i] = child;
        }

        node.estScore = maxScore;
        node.numDeaths += numDeaths;
      }
      return node;
#endif
    }
  }
}